<template>
    <div class="hello">
        <h3>概要</h3>
        <h3>ts基本数据类型 元祖 与数组 使用 与区别</h3>
        <h3>ts自动类型推断，类型断言</h3>
        <h3 style="color:red">interface的使用，声明类型，声明对象数组，普通数组，声明函数，可选参数，接口的extends与implements,索引签名</h3>
        <h3>never 与穷尽性检查</h3>
    </div>
</template>

<!--<script setup lang="ts" name="wf">   写不了name 属性  要安装  vite-plugin-vue-setup-extend-->
<script setup lang="ts" >
    let str:string = 'hello typescript'
    let num: number = 100
    let bool: boolean = true
    // let myName = 'Felixlu'
    // myName = 100  // error ts会自己自动判断类型赋值是否合格
    let arr: number[] = [1, 2, 3]  // number 数组声明
    // arr = ['a'] error // 类型错误
    let arr2: Array<number|string> = [1, 2, 3,"123"]  // 数组声明
    let arr3: [number,number,string] = [1, 123,"123"] // 元组类型  指定位置 指定类型
    console.log("arr2",arr2)
    console.log("arr3",arr3)

    let any :any = 10 // 可以赋值任意值 避免使用any

    // let str :string = undefined // 报错
    // let str2 :string = null // 报错

    /*interface 的使用*/
    interface A{
        name?:string,
        age?:string
    }
    /*合并效果*/
    interface A{
        height?:string
        weight?:string
        // hobby?:number[]
    }
    /*接口的继承*/
    interface Aa extends A{
        height?:string
        weight?:string
    }
    /*interface 直接实例化*/
    let a: A | null = {name:"Wf", age:"20",height:"180",weight:"89"}
    // a = null //可以
    console.log(a);
    // a2 = {}
    let aa:Aa | null =  {name:"Wf", age:"20",height:"180",weight:"89"}
    console.log(aa);
    /*interface 声明 对象数组*/
    interface Arr {
        // readonly id:number, // 只读属性的声明
        name:string
        // age:20  // 必须属性的声明 其他属性不管
        [props:string]:string |number | string[]  // 键必须是string, 值必须是string |number | string[]
        // [index:number]:any
    }
    let objArr:Arr[] = [{name:"wf"},{name:"wf2",age:"22",height:50,str:["a","b"]}] // 对象数组的定义
    console.log(objArr)

    let objx:Arr ={
        name:"wf",
        age:"20",
        info:["2","23"],
        // x:[2,3]
    }

    // 指定了--strictNullChecks标记，null和undefined只能赋值给void和它们各自。
    // 这能避免 很多常见的问题。 也许在某处你想传入一个 string或null或undefined，你可以使用联合类型string | null | undefined。
    // let und:undefined = undefined
    // let nullValue:null = null
    // let i:void = undefined
    // let jsss:void = null
    // console.log(und,nullValue,i,jsss) // undefined null undefined null
    // let z:string = undefined  // strictNullChecks 关掉后为编译器通过 一般不关
    // console.log("z",z);


    type union = number[] | null | undefined // 数组的 有值或者为null  ☆☆☆☆☆
    let un:union = [1,2,3,4,5]

    type union2 = Arr[] | null | undefined // 对象数组的 有值或者为null  联合了interface  ☆☆☆☆☆
    let un2:union2 = [{name:"wf"},{name:"wf2",age:"22",height:50,str:["a","b"]}]

    /*interface声明普通数组*/
    interface stringarr {
        [index:number]:number|string
    }
    let x123:stringarr = [10,20,"123"]
    console.log(x123)


    /*interface 声明函数  函数式接口*/
    interface AddFun {
        // name?:string
        // age?:number
        (a:number, b:number):number
    }
    let run2:AddFun = function (x, y):number{
        return x + y;
    };
    function test12(f:AddFun){
        console.log(f(10, 20));
    }
    test12(run2)
    // console.log(run2(10, 20));


    /*interface 混合函数与属性*/
    interface Counter {
        (start: number): string;
        interval: number;
        reset(): void;
    }

    // function getCounter(): Counter {
    //     let counter = <Counter>function (start: number) {
    //         console.log("start",start)
    //     };
    //     counter.interval = 123;
    //     counter.reset = function () {
    //         console.log("reset")
    //     };
    //     return counter; // 在函数身上加了属性和函数
    // }
    const fun = function():Counter {
        let counter = <Counter>function (start: number) :string{
            return "123"
        };
        counter.interval = 1234;
        counter.reset = function () { };
        return counter;// 在函数身上加了属性和函数
    }

    let c = fun();
    console.log(c);
    console.log(c.interval);
    console.log(c.reset);
    // c(10);
    // c.reset();
    // c.interval = 5.0;



    /*类实现 interface  */
    // interface Fun {
    //     run(a:number, b:number):number
    // }
    //
    // class jc implements Fun{
    //     run(a:number, b:number):number{
    //         return a+b
    //     }
    // }
    //





    type s = number[] | string // 类型谓词
    // let a4:s = [1,2,3]  // 可以
    // let a4:s = "123"    // 可以

    type num = 1 extends number ?1 : 0  //1
    type num2 = 1 extends never ?1 : 0  //0

    // Object object 和 {}
    let obj:Object = 100      // 一般都行
    // let obj2:Object =null  //null undefined报错

    // let obj3:object =true  不能是基本数据类型
    //  let obj3:{} = {
    //     a:10
    //  }
    // obj3.a = 100  不允许改变 一般不用

    // never
    type b = string & number // 自动推断为never 类型
    const error = function f(message:string) :never{  // 函数无返回值 只能抛出异常
        throw new Error(message)
    }
    // 死循环的函数
    const loop = function ():never {
        while (true){

        }
    }
    // 经典案例
    interface B{
       type:"a"
    }

    interface C{
        type:"b"
    }

    interface D{
        type:"c"
    }
    type All = B|C|D
    const cases = function (val:All) {
        switch (val.type) {
            case "a":
                break;
            case "b":
                break;
            case "c":
                // console.log("c")
                break; // 少写case c则代码会报错
            default:
                 const check:never = val;
                 break;
        }
    }
    cases({type:"c"})



    let bigNumber:bigint = BigInt(123456789) // 大数 声明 或者 根据es2020写成 => 123456789n
    // 唯一值类型 symbol不要传对象 、 数组
    let sym1:symbol = Symbol("name")
    let sym2:symbol = Symbol("name")
    let symbolObj = {
        [sym1]:"wf",
        [sym2]:"wf2", // 都有nanme 属性但是不会报错
        age:20
    }
    console.log(sym1==sym2) // false
    for (const key in symbolObj) {
        console.log(key)  // 找不到symbol类型的key  Object.keys 也获取不到
    }
    console.log(Reflect.ownKeys(symbolObj)) // 可以看到全部属性


    //never 与穷尽性检查
    interface Circle {kind: 'circle',radius: number}
    interface Square {kind: 'square',sideLength: number}
    interface Triangle {kind: 'triangle',sideLength: number}
    type Shape = Circle | Square | Triangle
    const xxx: Circle = {kind: 'circle', radius: 100}
    // 原本只有circle  | square 后面多了一个 Triangle ,导致default
    function getArea(shape: Shape) {
        switch(shape.kind) {
            case 'circle':return Math.PI * shape.radius ** 2
            case 'square':return shape.sideLength ** 2
            case 'triangle': return
            default:
            const _exhaustiveCheck: never = shape // 穷举完了吗  其实就是保证程序永远走不到default, 走到这就说明没有穷举完会报错
            // return _exhaustiveCheck
        }
    }
    console.log(getArea(xxx))


    // 高频面试 type 和interface 的区别
    // 1 interface只能定义对象类型，type声明可以定义基础类型、联合类型或交叉类型
    // 2 interface以extends、implements,从而扩展多个接口或类； type使用交叉类型&
    // 3 定义两个相同名称的接口会合并声明。定义两个同名的type会出现异常。


</script>

<style scoped>

</style>
